/**
 * @file FileIO.cpp
 * @brief 文件输入输出处理类的实现
 * 
 * 该类负责处理网格数据和属性数据的文件读写操作。
 * 包含了内存映射、高性能IO和数据解析等功能。
 */

#include "FileIO.h"
#include <fstream>
#include <sstream>
#include <iomanip>
#include <stdexcept>
#include <iostream>
#include <locale>

#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#endif

/**
 * @brief 检查文件是否存在且可访问
 * @param filename 要检查的文件路径
 * @return 文件存在且可访问时返回true
 * @throw std::runtime_error 当文件不存在或无法访问时抛出异常
 */
bool FileIO::checkFileExists(const std::string &filename)
{
    std::ifstream file(filename);
    if (!file)
    {
        throw std::runtime_error("文件不存在或无法访问: " + filename);
    }
    return true;
}

/**
 * @brief 内存映射文件类的构造函数
 * 
 * 初始化内存映射相关的句柄和指针
 */
FileIO::MemoryMappedFile::MemoryMappedFile() :
#ifdef _WIN32
    hFile(nullptr), hMapping(nullptr),
#else
    fd(-1), length(0),
#endif
    data(nullptr)
{
}

/**
 * @brief 内存映射文件类的析构函数
 * 
 * 负责清理内存映射和关闭文件句柄
 */
FileIO::MemoryMappedFile::~MemoryMappedFile()
{
#ifdef _WIN32
    if (data)
        UnmapViewOfFile(data);
    if (hMapping)
        CloseHandle(hMapping);
    if (hFile)
        CloseHandle(hFile);
#else
    if (data != MAP_FAILED && data != nullptr)
        munmap(data, length);
    if (fd != -1)
        close(fd);
#endif
}

/**
 * @brief 打开并映射文件到内存
 * @param filename 要映射的文件路径
 * @param fileSize 输出参数，返回文件大小
 * @return 映射成功返回true，失败返回false
 * 
 * 使用操作系统的内存映射机制将文件映射到内存空间，
 * 支持Windows和POSIX系统
 */
bool FileIO::MemoryMappedFile::open(const std::string &filename, size_t &fileSize)
{
#ifdef _WIN32
    hFile = CreateFileA(filename.c_str(),
                       GENERIC_READ,
                       FILE_SHARE_READ,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       NULL);

    if (hFile == INVALID_HANDLE_VALUE)
        return false;

    LARGE_INTEGER size;
    if (!GetFileSizeEx(hFile, &size))
    {
        CloseHandle(hFile);
        return false;
    }
    fileSize = size.QuadPart;

    hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (!hMapping)
    {
        CloseHandle(hFile);
        return false;
    }

    data = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
    if (!data)
    {
        CloseHandle(hMapping);
        CloseHandle(hFile);
        return false;
    }
#else
    fd = ::open(filename.c_str(), O_RDONLY);
    if (fd == -1)
        return false;

    struct stat sb;
    if (fstat(fd, &sb) == -1)
    {
        close(fd);
        return false;
    }

    fileSize = sb.st_size;
    length = fileSize;

    data = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, fd, 0);
    if (data == MAP_FAILED)
    {
        close(fd);
        data = nullptr;
        return false;
    }
#endif
    return true;
}

/**
 * @brief 获取映射的内存数据指针
 * @return 返回指向映射内存的指针
 */
void *FileIO::MemoryMappedFile::getData() const
{
    return data;
}

/**
 * @brief 读取网格文件
 * @param filename 网格文件路径
 * @param size 输出参数，网格尺寸
 * @param coord 输出参数，坐标数据
 * @param zcorn 输出参数，深度角点数据
 * @throw std::runtime_error 当文件读取或解析失败时抛出异常
 * 
 * 使用内存映射和高效的解析算法读取网格数据，
 * 支持SPECGRID、COORD和ZCORN关键字的解析
 */
void FileIO::readGridFile(const std::string &filename,
                          GridSize &size,
                          std::vector<std::vector<double>> &coord,
                          std::vector<double> &zcorn)
{
    size_t fileSize;
    MemoryMappedFile mmFile;

    if (!mmFile.open(filename, fileSize))
    {
        throw std::runtime_error("无法打开网格文件: " + filename);
    }

    char *current = static_cast<char *>(mmFile.getData());
    char *end = current + fileSize;

    // 预分配向量空间
    coord.clear();
    zcorn.clear();
    coord.reserve(1000000);  // 预估大小
    zcorn.reserve(8000000);  // 预估大小

    // 使用更大的缓冲区
    constexpr size_t PARSE_BUFFER_SIZE = 16 * 1024 * 1024;  // 16MB
    std::vector<char> buffer;
    buffer.reserve(PARSE_BUFFER_SIZE);

    std::string keyword;
    bool reading_data = false;
    
    // 使用线程本地存储加速数值解析
    thread_local std::vector<double> local_values;
    local_values.reserve(10000);

    while (current < end)
    {
        // 快速读取一行
        buffer.clear();
        char* line_start = current;
        while (current < end && *current != '\n' && *current != '\r')
        {
            current++;
        }
        buffer.assign(line_start, current);
        buffer.push_back('\0');

        // 跳过换行符
        while (current < end && (*current == '\n' || *current == '\r'))
            current++;

        // 快速处理行
        if (buffer[0] == '#' || buffer.empty())
            continue;

        char* line = buffer.data();
        while (*line == ' ' || *line == '\t') line++;

        if (*line == '/' || *line == '\0')
        {
            reading_data = false;
            keyword.clear();
            continue;
        }

        if (strncmp(line, "SPECGRID", 8) == 0)
        {
            keyword = "SPECGRID";
            reading_data = true;
            continue;
        }
        else if (strncmp(line, "COORD", 5) == 0)
        {
            keyword = "COORD";
            reading_data = true;
            continue;
        }
        else if (strncmp(line, "ZCORN", 5) == 0)
        {
            keyword = "ZCORN";
            reading_data = true;
            continue;
        }

        if (!reading_data || keyword.empty())
            continue;

        if (keyword == "SPECGRID")
        {
            int nx = 0, ny = 0, nz = 0;
            if (sscanf(line, "%d %d %d", &nx, &ny, &nz) >= 3)
            {
                size.Nx = nx;
                size.Ny = ny;
                size.Nz = nz;
            }
        }
        else if (keyword == "COORD" || keyword == "ZCORN")
        {
            local_values.clear();
            char* token = line;
            char* end_ptr;
            
            while (*token)
            {
                // 跳过空白字符
                while (*token == ' ' || *token == '\t') token++;
                if (!*token) break;

                if (token[0] == '*')
                {
                    // 处理重复值
                    int repeat = static_cast<int>(strtol(token - 1, &end_ptr, 10));
                    token = end_ptr + 1;
                    double value = strtod(token, &end_ptr);
                    
                    if (keyword == "COORD")
                    {
                        std::vector<double> point_coord(6);
                        for (int i = 0; i < 6; ++i)
                            point_coord[i] = value;
                        for (int i = 0; i < repeat; ++i)
                            coord.push_back(point_coord);
                    }
                    else
                    {
                        for (int i = 0; i < repeat; ++i)
                            zcorn.push_back(value);
                    }
                    token = end_ptr;
                }
                else
                {
                    // 处理单个值
                    double value = strtod(token, &end_ptr);
                    if (keyword == "COORD")
                    {
                        local_values.push_back(value);
                        if (local_values.size() == 6)
                        {
                            coord.push_back(std::vector<double>(
                                local_values.begin(), local_values.end()));
                            local_values.clear();
                        }
                    }
                    else
                    {
                        zcorn.push_back(value);
                    }
                    token = end_ptr;
                }
            }
        }
    }
}

/**
 * @brief 读取属性文件
 * @param filename 属性文件路径
 * @param props 输出参数，网格属性数据
 * @throw std::runtime_error 当文件读取或解析失败时抛出异常
 * 
 * 读取并解析网格属性文件，支持YOUNGMOD和POISSONR关键字，
 * 使用内存映射提高读取性能
 */
void FileIO::readPropertiesFile(const std::string &filename,
                               GridProperties &props)
{
    size_t fileSize;
    MemoryMappedFile mmFile;

    if (!mmFile.open(filename, fileSize))
    {
        throw std::runtime_error("无法打开属性文件: " + filename);
    }

    try
    {
        char *current = static_cast<char *>(mmFile.getData());
        char *end = current + fileSize;

        // 初始化数据结构
        props.youngmod.clear();
        props.poissonr.clear();

        std::string line;
        std::vector<char> buffer;
        buffer.reserve(256);

        bool reading_data = false;
        std::string keyword;

        while (current < end)
        {
            // 读取一行
            buffer.clear();
            while (current < end && *current != '\n' && *current != '\r')
            {
                buffer.push_back(*current++);
            }
            buffer.push_back('\0');

            // 跳过换行符
            while (current < end && (*current == '\n' || *current == '\r'))
            {
                current++;
            }

            line = std::string(buffer.data());
            if (line.empty())
                continue;

            // 移除首尾空白字符
            line.erase(0, line.find_first_not_of(" \t"));
            line.erase(line.find_last_not_of(" \t") + 1);

            // 移除注释
            size_t comment_pos = line.find("--");
            if (comment_pos != std::string::npos)
            {
                line = line.substr(0, comment_pos);
                line.erase(line.find_last_not_of(" \t") + 1);
            }

            if (line.empty())
                continue;

            if (line == "/")
            {
                reading_data = false;
                keyword.clear();
                continue;
            }

            if (line == "YOUNGMOD" || line == "POISSONR")
            {
                keyword = line;
                reading_data = true;
                continue;
            }

            if (reading_data && !keyword.empty())
            {
                processPropertyLine(line, keyword, props);
            }
        }

        if (props.youngmod.empty() || props.poissonr.empty())
        {
            throw std::runtime_error("属性数据不完整");
        }
    }
    catch (const std::exception &e)
    {
        throw std::runtime_error("处理属性文件时发生错误: " + std::string(e.what()));
    }
}

/**
 * @brief 处理属性数据行
 * @param line 要处理的数据行
 * @param keyword 当前处理的关键字
 * @param props 属性数据存储对象
 * 
 * 解析属性数据行，支持重复值语法(n*value)，
 * 将解析结果存储到对应的属性向量中
 */
void FileIO::processPropertyLine(const std::string &line,
                               const std::string &keyword,
                               GridProperties &props)
{
    std::istringstream iss(line);
    std::string value_str;
    while (iss >> value_str)
    {
        if (value_str.find('*') != std::string::npos)
        {
            size_t pos = value_str.find('*');
            int count = std::stoi(value_str.substr(0, pos));
            double value = std::stod(value_str.substr(pos + 1));

            if (keyword == "YOUNGMOD")
                props.youngmod.insert(props.youngmod.end(), count, value);
            else
                props.poissonr.insert(props.poissonr.end(), count, value);
        }
        else
        {
            double value = std::stod(value_str);
            if (keyword == "YOUNGMOD")
                props.youngmod.push_back(value);
            else
                props.poissonr.push_back(value);
        }
    }
}

/**
 * @brief 获取源文件的格式化字符串
 * @param filename 源文件路径
 * @return 返回格式化字符串
 */
std::string FileIO::getFormatFromSource(const std::string &filename)
{
    return " "; // 始终返回单个空格作为缩进
}

/**
 * @brief 写入属性值到文件
 * @param file 输出文件流
 * @param results 结果数据
 * @param isYoung 是否为杨氏模量数据
 * @param indent 缩进字符串
 * 
 * 使用缓冲区优化写入性能，支持数据压缩(使用重复值语法)
 */
void FileIO::writePropertyValues(std::ofstream &file,
                               const std::vector<Result> &results,
                               bool isYoung,
                               const std::string &indent)
{
    if (results.empty()) return;

    const int VALUES_PER_LINE = 6;
    const int BUFFER_SIZE = 1024 * 1024;  // 1MB 缓冲区
    std::string buffer;
    buffer.reserve(BUFFER_SIZE);

    // 使用字符串流进行格式化，避免频繁的I/O操作
    std::ostringstream oss;
    oss.imbue(std::locale("C"));
    oss << std::fixed << std::setprecision(6);

    size_t count = 0;
    double current_value = isYoung ? results[0].young_value : results[0].poisson_value;
    
    for (size_t i = 0; i < results.size(); ++i)  // 修改循环条件
    {
        double next_value = isYoung ? results[i].young_value : results[i].poisson_value;
        
        if (i > 0 && std::abs(next_value - current_value) < 1e-10)
        {
            count++;
        }
        else
        {
            if (i > 0)  // 不是第一个值时才输出前一组
            {
                if (count > 0)
                {
                    oss << " " << (count + 1) << "*" << current_value;
                }
                else
                {
                    oss << " 1*" << current_value;
                }

                // 每行末尾或缓冲区满时写入
                if ((i % VALUES_PER_LINE) == 0)
                {
                    oss << "\n";
                    buffer += oss.str();
                    oss.str("");
                    
                    if (buffer.size() >= BUFFER_SIZE)
                    {
                        file << buffer;
                        buffer.clear();
                        buffer.reserve(BUFFER_SIZE);
                    }
                }
            }
            current_value = next_value;
            count = 0;
        }
    }

    // 处理最后一组值
    if (count > 0)
    {
        oss << " " << (count + 1) << "*" << current_value;
    }
    else
    {
        oss << " 1*" << current_value;
    }
    oss << "\n";
    buffer += oss.str();

    // 确保写入所有数据
    if (!buffer.empty())
    {
        file << buffer;
        file.flush();  // 确保所有数据都写入文件
    }
}

/**
 * @brief 保存插值结果到文件
 * @param filename 输出文件路径
 * @param results 插值结果数据
 * @throw std::runtime_error 当文件创建或写入失败时抛出异常
 * 
 * 使用二进制模式和大缓冲区优化写入性能，
 * 输出格式符合Eclipse格式规范
 */
void FileIO::saveResults(const std::string &filename,
                        const std::vector<Result> &results)
{
    // 使用二进制模式打开文件，提高写入速度
    std::ofstream file(filename, std::ios::binary);
    if (!file)
    {
        throw std::runtime_error("无法创建输出文件: " + filename);
    }

    // 设置文件缓冲区
    const int BUFFER_SIZE = 8 * 1024 * 1024;  // 增加到8MB
    std::unique_ptr<char[]> buffer(new char[BUFFER_SIZE]);  // 使用智能指针
    file.rdbuf()->pubsetbuf(buffer.get(), BUFFER_SIZE);

    file << "YOUNGMOD\n";
    writePropertyValues(file, results, true, " ");
    file << "/\n\n";

    file << "POISSONR\n";
    writePropertyValues(file, results, false, " ");
    file << "/\n";

    // 确保所有数据都写入文件
    file.flush();
    file.close();
}